Word Ladder II

Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the word list

For example,

Given:

beginWord = "hit"

endWord = "cog"

wordList = ["hot","dot","dog","lot","log"]

Return

  1. [
  2. ["hit","hot","dot","dog","cog"],
  3. ["hit","hot","lot","log","cog"]
  4. ]

Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.

Solution:

  1. public class Solution {
  2. public List<List<String>> findLadders(String start, String end, Set<String> dict) {
  3. // hash set for both ends
  4. Set<String> set1 = new HashSet<String>();
  5. Set<String> set2 = new HashSet<String>();
  6. // initial words in both ends
  7. set1.add(start);
  8. set2.add(end);
  9. // we use a map to help construct the final result
  10. Map<String, List<String>> map = new HashMap<String, List<String>>();
  11. // build the map
  12. helper(dict, set1, set2, map, false);
  13. List<List<String>> res = new ArrayList<List<String>>();
  14. List<String> sol = new ArrayList<String>(Arrays.asList(start));
  15. // recursively build the final result
  16. generateList(start, end, map, sol, res);
  17. return res;
  18. }
  19. boolean helper(Set<String> dict, Set<String> set1, Set<String> set2, Map<String, List<String>> map, boolean flip) {
  20. if (set1.isEmpty()) {
  21. return false;
  22. }
  23. if (set1.size() > set2.size()) {
  24. return helper(dict, set2, set1, map, !flip);
  25. }
  26. // remove words on current both ends from the dict
  27. dict.removeAll(set1);
  28. dict.removeAll(set2);
  29. // as we only need the shortest paths
  30. // we use a boolean value help early termination
  31. boolean done = false;
  32. // set for the next level
  33. Set<String> set = new HashSet<String>();
  34. // for each string in end 1
  35. for (String str : set1) {
  36. for (int i = 0; i < str.length(); i++) {
  37. char[] chars = str.toCharArray();
  38. // change one character for every position
  39. for (char ch = 'a'; ch <= 'z'; ch++) {
  40. chars[i] = ch;
  41. String word = new String(chars);
  42. // make sure we construct the tree in the correct direction
  43. String key = flip ? word : str;
  44. String val = flip ? str : word;
  45. List<String> list = map.containsKey(key) ? map.get(key) : new ArrayList<String>();
  46. if (set2.contains(word)) {
  47. done = true;
  48. list.add(val);
  49. map.put(key, list);
  50. }
  51. if (dict.contains(word)) {
  52. set.add(word);
  53. list.add(val);
  54. map.put(key, list);
  55. }
  56. }
  57. }
  58. }
  59. // early terminate if done is true
  60. return done || helper(dict, set2, set, map, !flip);
  61. }
  62. void generateList(String start, String end, Map<String, List<String>> map, List<String> sol, List<List<String>> res) {
  63. if (start.equals(end)) {
  64. res.add(new ArrayList<String>(sol));
  65. return;
  66. }
  67. // need this check in case the diff between start and end happens to be one
  68. // e.g "a", "c", {"a", "b", "c"}
  69. if (!map.containsKey(start)) {
  70. return;
  71. }
  72. for (String word : map.get(start)) {
  73. sol.add(word);
  74. generateList(word, end, map, sol, res);
  75. sol.remove(sol.size() - 1);
  76. }
  77. }
  78. }